library(dplyr)
library(ggplot2)
library(plotly)
data_file <- read.csv("cleaned_online_retail.csv")
sample(data_file)
str(data_file)
'data.frame': 512974 obs. of 14 variables:
$ Invoice : chr "489434" "489434" "489434" "489434" ...
$ StockCode : chr "85048" "79323P" "79323W" "22041" ...
$ Description : chr "15CM CHRISTMAS GLASS BALL 20 LIGHTS" "PINK CHERRY LIGHTS" "WHITE CHERRY LIGHTS" "RECORD FRAME 7\" SINGLE SIZE" ...
$ Quantity : int 12 12 12 48 24 24 24 10 12 12 ...
$ InvoiceDate : chr "2009-12-01 07:45:00" "2009-12-01 07:45:00" "2009-12-01 07:45:00" "2009-12-01 07:45:00" ...
$ Price : num 6.95 6.75 6.75 2.1 1.25 1.65 1.25 5.95 2.55 3.75 ...
$ Customer.ID : int 13085 13085 13085 13085 13085 13085 13085 13085 13085 13085 ...
$ Country : chr "United Kingdom" "United Kingdom" "United Kingdom" "United Kingdom" ...
$ TotalPrice : num 83.4 81 81 100.8 30 ...
$ z_score : num 0.0155 0.0141 0.0141 -0.0177 -0.0235 ...
$ z_score_quantity : num 0.00302 0.00302 0.00302 0.39017 0.13207 ...
$ z_score_total_price: num 0.976 0.939 0.939 1.242 0.16 ...
$ Quantity_capped : int 12 12 12 32 24 24 24 10 12 12 ...
$ TotalPrice_capped : num 60 60 60 60 30 39.6 30 59.5 30.6 45 ...
# Convert relevant columns to factors
data_file$Country <- as.factor(data_file$Country)
data_file$StockCode <- as.factor(data_file$StockCode)
# Converting integer to numeric
data_file$Quantity <- as.numeric(data_file$Quantity)
names(data_file)
[1] "Invoice" "StockCode"
[3] "Description" "Quantity"
[5] "InvoiceDate" "Price"
[7] "Customer.ID" "Country"
[9] "TotalPrice" "z_score"
[11] "z_score_quantity" "z_score_total_price"
[13] "Quantity_capped" "TotalPrice_capped"
# removing the unnecessary columns from the cleaned dataset
visualize_df <- data_file %>%
select(Invoice, StockCode, Description, Quantity, InvoiceDate, Price, TotalPrice, Customer.ID, Country)
visualize_df <- write.csv(visualize_df, "visualize_df.csv", row.names = FALSE)
visualize_df <- read.csv("visualize_df.csv")
head(visualize_df)
summary(visualize_df)
Invoice StockCode Description
Length:512974 Length:512974 Length:512974
Class :character Class :character Class :character
Mode :character Mode :character Mode :character
Quantity InvoiceDate Price
Min. : 1.00 Length:512974 Min. : 0.000
1st Qu.: 1.00 Class :character 1st Qu.: 1.250
Median : 3.00 Mode :character Median : 2.100
Mean : 11.72 Mean : 3.643
3rd Qu.: 11.00 3rd Qu.: 4.210
Max. :19152.00 Max. :441.100
TotalPrice Customer.ID Country
Min. : 0.00 Min. :12346 Length:512974
1st Qu.: 3.90 1st Qu.:13997 Class :character
Median : 10.12 Median :15321 Mode :character
Mean : 19.49 Mean :15369
3rd Qu.: 17.70 3rd Qu.:16814
Max. :15818.40 Max. :18287
NA's :105320
DATA VISUALIZATION:
# Visualizing the distribution of Quantity
vis_quantity <- ggplot(visualize_df, aes(log(Quantity+1))) +
geom_histogram(bins = 50, fill = "blue", color = "white") +
labs(title = "Distribution of Quantity Sold", x = "Quantity", y = "Density")
ggplotly(vis_quantity)
Observation: The histogram shows a skewed distribution, indicating
that most products are sold in small quantities, with a few products
having significantly higher quantities.
Interpretation: The majority of items in the store are sold in small
volumes, which might be typical for a wide-ranging product catalog.
However, there are occasional bulk purchases, reflected in the long tail
of the distribution.
# Visualizing the distribution of Price
vis_price <- ggplot(visualize_df, aes(log(Price+1))) +
geom_histogram(bins = 50, fill = "blue", color = "white") +
labs(title = "Distribution of Product Prices", x = "Price", y = "Frequency")
ggplotly(vis_price)
Observation: The distribution of product prices reveals that most
items are priced lower, with few high-priced products.
Interpretation: The store mainly sells low-priced items, and
higher-priced items are outliers, making up a small portion of the total
products offered.
# Visualizing the distribution of TotalPrice (Revenue)
vis_total_price <- ggplot(visualize_df, aes(x = log(TotalPrice+1))) +
geom_histogram(bins = 50, fill = "blue", color = "white") +
labs(title = "Distribution of Total Transaction Values (TotalPrice)", x = "TotalPrice", y = "Frequency") +
theme_classic()
ggplotly(vis_total_price)
Observation: Most transactions fall in the lower range of total
price, with only a few transactions generating higher total
revenue.
Interpretation: Similar to individual product prices, total
transaction amounts are concentrated around lower values, reflecting
frequent small purchases and occasional larger sales.
# Density plot for Quantity
vis_qunatity_dens <- ggplot(visualize_df, aes(log(Quantity)+1)) +
geom_density(fill = "blue", alpha = 0.5) +
labs(title = "Density Plot of Quantity Sold", x = "Quantity", y = "Density")
ggplotly(vis_qunatity_dens)
Observation: The density plot highlights the peak at lower
quantities sold.
Interpretation: The majority of sales involve fewer units,
suggesting a high frequency of single or small quantity purchases.
# Density plot for Price
vis_price_dens <- ggplot(visualize_df, aes(log(Price+1))) +
geom_density(fill = "blue", alpha = 0.5) +
labs(title = "Density Plot of Product Prices", x = "Price", y = "Density")
ggplotly(vis_price_dens)
Observation: The majority of products are priced in the lower range,
with a smooth decline toward higher-priced products.
Interpretation: Most items are inexpensive, reflecting a retail
strategy focused on affordability, with a few higher-priced items
possibly driving luxury or special purchases.
# Density plot for TotalPrice
vis_total_price_dens <- ggplot(visualize_df, aes(log(TotalPrice)+1)) +
geom_density(fill = "blue", alpha = 0.5) +
labs(title = "Density Plot of Total Transaction Values (TotalPrice)", x = "TotalPrice", y = "Density")
ggplotly(vis_total_price_dens)
Warning: Removed 1566 rows containing non-finite outside the scale
range (`stat_density()`).
Observation: The density plot for total transaction values shows
that most transactions involve lower revenue.
Interpretation: Customers tend to make smaller purchases more
frequently, contributing to the overall sales pattern of the store.
CUSTOMER ANALYSIS
# Top 10 customers by total spending
top_customers <- visualize_df %>%
group_by(Customer.ID) %>%
summarise(TotalSpent = sum(TotalPrice)) %>%
arrange(desc(TotalSpent)) %>%
head(10)
# Plot Top 10 Customers by Total Spending
vis_top_custmers <- ggplot(top_customers, aes(x = reorder(Customer.ID, TotalSpent), y = TotalSpent)) +
geom_bar(stat = "identity", fill = "blue") +
coord_flip() +
labs(title = "Top 10 Customers by Total Spending", x = "Customer ID", y = "Total Spending") +
theme_classic()
# Convert to interactive plot using ggplotly
ggplotly(vis_top_custmers)
NA
Observation: The top 10 customers contribute a significant portion
of the total revenue.
Interpretation: A small number of high-value customers are
responsible for a large share of sales, which could indicate the
importance of customer retention and loyalty programs.
PRODUCT ANALYSIS
# Top 10 products by quantity sold
top_products_quantity <- visualize_df %>%
group_by(StockCode, Description) %>%
summarise(TotalQuantity = sum(Quantity), .groups = "drop") %>%
arrange(desc(TotalQuantity)) %>%
head(10)
# Plot Top 10 Products by Quantity Sold
vis_top_products_quantity <- ggplot(top_products_quantity, aes(x = reorder(Description, TotalQuantity), y = TotalQuantity)) +
geom_bar(stat = "identity", fill = "yellow") +
coord_flip() +
labs(title = "Top 10 Products by Quantity Sold", x = "Product Description", y = "Total Quantity Sold") +
theme_classic()
# Convert to interactive plot using ggplotly
ggplotly(vis_top_products_quantity)
Observation: A small set of products make up a large proportion of
the total quantity sold.
Interpretation: These products are likely popular or frequently
purchased in bulk, indicating their importance in driving sales volume
for the store.
# Top 10 products by total revenue (TotalPrice)
top_products_revenue <- visualize_df %>%
group_by(StockCode, Description) %>%
summarise(TotalRevenue = sum(TotalPrice), .groups = "drop") %>%
arrange(desc(TotalRevenue)) %>%
head(10)
# Plot Top 10 Products by Total Revenue
vis_top_product_revenue <- ggplot(top_products_revenue, aes(x = reorder(Description, TotalRevenue), y = TotalRevenue)) +
geom_bar(stat = "identity", fill = "purple") +
coord_flip() +
labs(title = "Top 10 Products by Revenue", x = "Product Description", y = "Total Revenue") +
theme_classic()
# Convert to interactive plot using ggplotly
ggplotly(vis_top_product_revenue)
Observation: A few products contribute disproportionately to the
total revenue.
Observation: The majority of sales are concentrated in a small
number of countries.
Interpretation: The store likely operates in key markets where it
has established strong customer bases, but may have opportunities to
expand sales in underrepresented regions.
TIME SERIES ANALYSIS
# Convert InvoiceDate from character to POSIXct format
visualize_df$InvoiceDate <- as.POSIXct(visualize_df$InvoiceDate, format = "%Y-%m-%d %H:%M:%S")
# Extract just the date (without time) for daily aggregation
visualize_df$DateOnly <- as.Date(visualize_df$InvoiceDate)
# Aggregate the data by day to calculate total sales per day
daily_sales <- visualize_df %>%
group_by(DateOnly) %>%
summarise(TotalSales = sum(TotalPrice))
# Take a look at the first few rows
head(daily_sales)
# Create a line plot of total sales over time (daily)
vis_date_daily <- ggplot(daily_sales, aes(x = DateOnly, y = TotalSales)) +
geom_line(color = "green", linewidth = 0.5) +
labs(title = "Total Sales Over Time (Daily)", x = "Date", y = "Total Sales (Revenue)") +
theme_classic()
# Convert to an interactive plot using ggplotly
ggplotly(vis_date_daily)
NA
Observation: The plot shows fluctuations in daily sales, with
noticeable peaks during certain periods.
Observation: The monthly sales plot smooths out the daily
fluctuations, showing broader trends and potential seasonality in the
data.
Interpretation: The data indicates periods of steady growth and
possible seasonal spikes, useful for forecasting future sales and
managing inventory.
# Create a line plot of total sales over time (daily)
vis_date_daily_sm <- ggplot(daily_sales, aes(x = DateOnly, y = TotalSales)) +
geom_line(color = "green", size = 0.5) +
geom_smooth(method = "loess", color = "darkgreen", size = 0.3) +
labs(title = "Total Sales Over Time (Daily)", x = "Date", y = "Total Sales (Revenue)") +
theme_classic()
# Convert to an interactive plot using ggplotly
ggplotly(vis_date_daily_sm)
`geom_smooth()` using formula = 'y ~ x'
Observation: The line plot with the smoothing line shows
fluctuations in daily sales, with some distinct peaks.
Observation: Sales traffic peaks on certain days of the week, with
noticeably higher activity.
Observation: Sales activity varies throughout the day, with peaks at
certain hours.
Observation: Sales trends vary across the top 5 countries, with
certain periods showing synchronized peaks across multiple
countries.
Interpretation: This analysis reveals global sales trends and
highlights the most active markets during specific periods. Insights can
be used to identify country-specific demand cycles and adjust inventory
or marketing efforts accordingly.
# Categorize customers based on total spending
customer_segments <- visualize_df %>%
group_by(Customer.ID) %>%
summarise(TotalSpent = sum(TotalPrice)) %>%
mutate(Segment = case_when(
TotalSpent < 100 ~ "Low Spenders",
TotalSpent >= 100 & TotalSpent < 500 ~ "Medium Spenders",
TotalSpent >= 500 ~ "High Spenders"
))
# Visualize the number of customers in each segment
vis_customer_segments <- ggplot(customer_segments, aes(x = Segment, fill = Segment)) +
geom_bar() +
labs(title = "Customer Segments Based on Total Spending", x = "Segment", y = "Number of Customers") +
theme_classic()
# Convert to an interactive plot using ggplotly
ggplotly(vis_customer_segments)
NA
Observation: The majority of customers are low spenders, with fewer
medium and high spenders.
Interpretation: The store has a broad customer base, but most of the
revenue is likely coming from a small number of high-spending customers.
Retaining high spenders and converting more low spenders to medium or
high spenders should be a priority.
# Count the number of orders per product (instead of total quantity sold)
top_products_orders <- visualize_df %>%
group_by(StockCode, Description) %>%
summarise(OrderCount = n(), .groups = "drop") %>%
arrange(desc(OrderCount)) %>%
head(10)
# Plot top products based on number of orders
vis_top_products_orders <- ggplot(top_products_orders, aes(x = reorder(Description, OrderCount), y = OrderCount)) +
geom_bar(stat = "identity", fill = "orange") +
coord_flip() +
labs(title = "Top Products Based on Number of Orders", x = "Product Description", y = "Number of Orders") +
theme_classic()
# Convert to an interactive plot using ggplotly
ggplotly(vis_top_products_orders)
NA
Observation: A few products are ordered much more frequently than
others.
Interpretation: These products are popular with customers and likely
represent essential or fast-selling items. The store may benefit from
stocking these items in larger quantities and promoting them more to
maintain and grow sales.
LS0tDQp0aXRsZTogImRhdGEgdmlzdWFsaXphdGlvbiBmb3IgcmV0YWlsIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCmBgYHtyfQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkocGxvdGx5KQ0KYGBgDQoNCmBgYHtyfQ0KZGF0YV9maWxlIDwtIHJlYWQuY3N2KCJjbGVhbmVkX29ubGluZV9yZXRhaWwuY3N2IikNCmBgYA0KDQpgYGB7cn0NCnNhbXBsZShkYXRhX2ZpbGUpDQpgYGANCg0KYGBge3J9DQpzdHIoZGF0YV9maWxlKQ0KYGBgDQpgYGB7cn0NCiMgQ29udmVydCByZWxldmFudCBjb2x1bW5zIHRvIGZhY3RvcnMNCmRhdGFfZmlsZSRDb3VudHJ5IDwtIGFzLmZhY3RvcihkYXRhX2ZpbGUkQ291bnRyeSkNCmRhdGFfZmlsZSRTdG9ja0NvZGUgPC0gYXMuZmFjdG9yKGRhdGFfZmlsZSRTdG9ja0NvZGUpDQoNCiMgQ29udmVydGluZyBpbnRlZ2VyIHRvIG51bWVyaWMNCmRhdGFfZmlsZSRRdWFudGl0eSA8LSBhcy5udW1lcmljKGRhdGFfZmlsZSRRdWFudGl0eSkNCmBgYA0KDQpgYGB7cn0NCm5hbWVzKGRhdGFfZmlsZSkNCmBgYA0KDQpgYGB7cn0NCiMgcmVtb3ZpbmcgdGhlIHVubmVjZXNzYXJ5IGNvbHVtbnMgZnJvbSB0aGUgY2xlYW5lZCBkYXRhc2V0DQp2aXN1YWxpemVfZGYgPC0gZGF0YV9maWxlICU+JSANCiAgc2VsZWN0KEludm9pY2UsIFN0b2NrQ29kZSwgRGVzY3JpcHRpb24sIFF1YW50aXR5LCBJbnZvaWNlRGF0ZSwgUHJpY2UsIFRvdGFsUHJpY2UsIEN1c3RvbWVyLklELCBDb3VudHJ5KQ0KYGBgDQoNCmBgYHtyfQ0KdmlzdWFsaXplX2RmIDwtIHdyaXRlLmNzdih2aXN1YWxpemVfZGYsICJ2aXN1YWxpemVfZGYuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpDQpgYGANCmBgYHtyfQ0KdmlzdWFsaXplX2RmIDwtIHJlYWQuY3N2KCJ2aXN1YWxpemVfZGYuY3N2IikNCmBgYA0KDQpgYGB7cn0NCmhlYWQodmlzdWFsaXplX2RmKQ0KYGBgDQpgYGB7cn0NCnN1bW1hcnkodmlzdWFsaXplX2RmKQ0KYGBgDQoNCg0KIyMjIERBVEEgVklTVUFMSVpBVElPTjogIyMjDQoNCmBgYHtyfQ0KIyBWaXN1YWxpemluZyB0aGUgZGlzdHJpYnV0aW9uIG9mIFF1YW50aXR5DQp2aXNfcXVhbnRpdHkgPC0gZ2dwbG90KHZpc3VhbGl6ZV9kZiwgYWVzKGxvZyhRdWFudGl0eSsxKSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDUwLCBmaWxsID0gImJsdWUiLCBjb2xvciA9ICJ3aGl0ZSIpICsNCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgUXVhbnRpdHkgU29sZCIsIHggPSAiUXVhbnRpdHkiLCB5ID0gIkRlbnNpdHkiKQ0KZ2dwbG90bHkodmlzX3F1YW50aXR5KQ0KYGBgDQojIE9ic2VydmF0aW9uOiBUaGUgaGlzdG9ncmFtIHNob3dzIGEgc2tld2VkIGRpc3RyaWJ1dGlvbiwgaW5kaWNhdGluZyB0aGF0IG1vc3QgcHJvZHVjdHMgYXJlIHNvbGQgaW4gc21hbGwgcXVhbnRpdGllcywgd2l0aCBhIGZldyBwcm9kdWN0cyBoYXZpbmcgc2lnbmlmaWNhbnRseSBoaWdoZXIgcXVhbnRpdGllcy4NCiMgSW50ZXJwcmV0YXRpb246IFRoZSBtYWpvcml0eSBvZiBpdGVtcyBpbiB0aGUgc3RvcmUgYXJlIHNvbGQgaW4gc21hbGwgdm9sdW1lcywgd2hpY2ggbWlnaHQgYmUgdHlwaWNhbCBmb3IgYSB3aWRlLXJhbmdpbmcgcHJvZHVjdCBjYXRhbG9nLiBIb3dldmVyLCB0aGVyZSBhcmUgb2NjYXNpb25hbCBidWxrIHB1cmNoYXNlcywgcmVmbGVjdGVkIGluIHRoZSBsb25nIHRhaWwgb2YgdGhlIGRpc3RyaWJ1dGlvbi4NCg0KYGBge3J9DQojIFZpc3VhbGl6aW5nIHRoZSBkaXN0cmlidXRpb24gb2YgUHJpY2UNCnZpc19wcmljZSA8LSBnZ3Bsb3QodmlzdWFsaXplX2RmLCBhZXMobG9nKFByaWNlKzEpKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gNTAsIGZpbGwgPSAiYmx1ZSIsIGNvbG9yID0gIndoaXRlIikgKw0KICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBQcm9kdWN0IFByaWNlcyIsIHggPSAiUHJpY2UiLCB5ID0gIkZyZXF1ZW5jeSIpDQpnZ3Bsb3RseSh2aXNfcHJpY2UpDQpgYGANCiMgT2JzZXJ2YXRpb246IFRoZSBkaXN0cmlidXRpb24gb2YgcHJvZHVjdCBwcmljZXMgcmV2ZWFscyB0aGF0IG1vc3QgaXRlbXMgYXJlIHByaWNlZCBsb3dlciwgd2l0aCBmZXcgaGlnaC1wcmljZWQgcHJvZHVjdHMuDQojIEludGVycHJldGF0aW9uOiBUaGUgc3RvcmUgbWFpbmx5IHNlbGxzIGxvdy1wcmljZWQgaXRlbXMsIGFuZCBoaWdoZXItcHJpY2VkIGl0ZW1zIGFyZSBvdXRsaWVycywgbWFraW5nIHVwIGEgc21hbGwgcG9ydGlvbiBvZiB0aGUgdG90YWwgcHJvZHVjdHMgb2ZmZXJlZC4NCg0KYGBge3J9DQojIFZpc3VhbGl6aW5nIHRoZSBkaXN0cmlidXRpb24gb2YgVG90YWxQcmljZSAoUmV2ZW51ZSkNCnZpc190b3RhbF9wcmljZSA8LSBnZ3Bsb3QodmlzdWFsaXplX2RmLCBhZXMoeCA9IGxvZyhUb3RhbFByaWNlKzEpKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gNTAsIGZpbGwgPSAiYmx1ZSIsIGNvbG9yID0gIndoaXRlIikgKw0KICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBUb3RhbCBUcmFuc2FjdGlvbiBWYWx1ZXMgKFRvdGFsUHJpY2UpIiwgeCA9ICJUb3RhbFByaWNlIiwgeSA9ICJGcmVxdWVuY3kiKSArIA0KICB0aGVtZV9jbGFzc2ljKCkNCmdncGxvdGx5KHZpc190b3RhbF9wcmljZSkNCmBgYA0KIyBPYnNlcnZhdGlvbjogTW9zdCB0cmFuc2FjdGlvbnMgZmFsbCBpbiB0aGUgbG93ZXIgcmFuZ2Ugb2YgdG90YWwgcHJpY2UsIHdpdGggb25seSBhIGZldyB0cmFuc2FjdGlvbnMgZ2VuZXJhdGluZyBoaWdoZXIgdG90YWwgcmV2ZW51ZS4NCiMgSW50ZXJwcmV0YXRpb246IFNpbWlsYXIgdG8gaW5kaXZpZHVhbCBwcm9kdWN0IHByaWNlcywgdG90YWwgdHJhbnNhY3Rpb24gYW1vdW50cyBhcmUgY29uY2VudHJhdGVkIGFyb3VuZCBsb3dlciB2YWx1ZXMsIHJlZmxlY3RpbmcgZnJlcXVlbnQgc21hbGwgcHVyY2hhc2VzIGFuZCBvY2Nhc2lvbmFsIGxhcmdlciBzYWxlcy4NCg0KYGBge3J9DQojIERlbnNpdHkgcGxvdCBmb3IgUXVhbnRpdHkNCnZpc19xdW5hdGl0eV9kZW5zIDwtIGdncGxvdCh2aXN1YWxpemVfZGYsIGFlcyhsb2coUXVhbnRpdHkpKzEpKSArDQogIGdlb21fZGVuc2l0eShmaWxsID0gImJsdWUiLCBhbHBoYSA9IDAuNSkgKw0KICBsYWJzKHRpdGxlID0gIkRlbnNpdHkgUGxvdCBvZiBRdWFudGl0eSBTb2xkIiwgeCA9ICJRdWFudGl0eSIsIHkgPSAiRGVuc2l0eSIpDQpnZ3Bsb3RseSh2aXNfcXVuYXRpdHlfZGVucykNCmBgYA0KIyBPYnNlcnZhdGlvbjogVGhlIGRlbnNpdHkgcGxvdCBoaWdobGlnaHRzIHRoZSBwZWFrIGF0IGxvd2VyIHF1YW50aXRpZXMgc29sZC4NCiMgSW50ZXJwcmV0YXRpb246IFRoZSBtYWpvcml0eSBvZiBzYWxlcyBpbnZvbHZlIGZld2VyIHVuaXRzLCBzdWdnZXN0aW5nIGEgaGlnaCBmcmVxdWVuY3kgb2Ygc2luZ2xlIG9yIHNtYWxsIHF1YW50aXR5IHB1cmNoYXNlcy4NCg0KYGBge3J9DQojIERlbnNpdHkgcGxvdCBmb3IgUHJpY2UNCnZpc19wcmljZV9kZW5zIDwtIGdncGxvdCh2aXN1YWxpemVfZGYsIGFlcyhsb2coUHJpY2UrMSkpKSArDQogIGdlb21fZGVuc2l0eShmaWxsID0gImJsdWUiLCBhbHBoYSA9IDAuNSkgKw0KICBsYWJzKHRpdGxlID0gIkRlbnNpdHkgUGxvdCBvZiBQcm9kdWN0IFByaWNlcyIsIHggPSAiUHJpY2UiLCB5ID0gIkRlbnNpdHkiKQ0KZ2dwbG90bHkodmlzX3ByaWNlX2RlbnMpDQpgYGANCiMgT2JzZXJ2YXRpb246IFRoZSBtYWpvcml0eSBvZiBwcm9kdWN0cyBhcmUgcHJpY2VkIGluIHRoZSBsb3dlciByYW5nZSwgd2l0aCBhIHNtb290aCBkZWNsaW5lIHRvd2FyZCBoaWdoZXItcHJpY2VkIHByb2R1Y3RzLg0KIyBJbnRlcnByZXRhdGlvbjogTW9zdCBpdGVtcyBhcmUgaW5leHBlbnNpdmUsIHJlZmxlY3RpbmcgYSByZXRhaWwgc3RyYXRlZ3kgZm9jdXNlZCBvbiBhZmZvcmRhYmlsaXR5LCB3aXRoIGEgZmV3IGhpZ2hlci1wcmljZWQgaXRlbXMgcG9zc2libHkgZHJpdmluZyBsdXh1cnkgb3Igc3BlY2lhbCBwdXJjaGFzZXMuDQoNCmBgYHtyfQ0KIyBEZW5zaXR5IHBsb3QgZm9yIFRvdGFsUHJpY2UNCnZpc190b3RhbF9wcmljZV9kZW5zIDwtIGdncGxvdCh2aXN1YWxpemVfZGYsIGFlcyhsb2coVG90YWxQcmljZSkrMSkpICsNCiAgZ2VvbV9kZW5zaXR5KGZpbGwgPSAiYmx1ZSIsIGFscGhhID0gMC41KSArDQogIGxhYnModGl0bGUgPSAiRGVuc2l0eSBQbG90IG9mIFRvdGFsIFRyYW5zYWN0aW9uIFZhbHVlcyAoVG90YWxQcmljZSkiLCB4ID0gIlRvdGFsUHJpY2UiLCB5ID0gIkRlbnNpdHkiKQ0KDQpnZ3Bsb3RseSh2aXNfdG90YWxfcHJpY2VfZGVucykNCmBgYA0KIyBPYnNlcnZhdGlvbjogVGhlIGRlbnNpdHkgcGxvdCBmb3IgdG90YWwgdHJhbnNhY3Rpb24gdmFsdWVzIHNob3dzIHRoYXQgbW9zdCB0cmFuc2FjdGlvbnMgaW52b2x2ZSBsb3dlciByZXZlbnVlLg0KIyBJbnRlcnByZXRhdGlvbjogQ3VzdG9tZXJzIHRlbmQgdG8gbWFrZSBzbWFsbGVyIHB1cmNoYXNlcyBtb3JlIGZyZXF1ZW50bHksIGNvbnRyaWJ1dGluZyB0byB0aGUgb3ZlcmFsbCBzYWxlcyBwYXR0ZXJuIG9mIHRoZSBzdG9yZS4NCg0KDQojIyMgQ1VTVE9NRVIgQU5BTFlTSVMgIyMjDQogIA0KYGBge3J9DQojIFRvcCAxMCBjdXN0b21lcnMgYnkgdG90YWwgc3BlbmRpbmcNCnRvcF9jdXN0b21lcnMgPC0gdmlzdWFsaXplX2RmICU+JQ0KICBncm91cF9ieShDdXN0b21lci5JRCkgJT4lDQogIHN1bW1hcmlzZShUb3RhbFNwZW50ID0gc3VtKFRvdGFsUHJpY2UpKSAlPiUNCiAgYXJyYW5nZShkZXNjKFRvdGFsU3BlbnQpKSAlPiUNCiAgaGVhZCgxMCkNCiAgDQojIFBsb3QgVG9wIDEwIEN1c3RvbWVycyBieSBUb3RhbCBTcGVuZGluZw0KdmlzX3RvcF9jdXN0bWVycyA8LSBnZ3Bsb3QodG9wX2N1c3RvbWVycywgYWVzKHggPSByZW9yZGVyKEN1c3RvbWVyLklELCBUb3RhbFNwZW50KSwgeSA9IFRvdGFsU3BlbnQpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gImJsdWUiKSArDQogIGNvb3JkX2ZsaXAoKSArDQogIGxhYnModGl0bGUgPSAiVG9wIDEwIEN1c3RvbWVycyBieSBUb3RhbCBTcGVuZGluZyIsIHggPSAiQ3VzdG9tZXIgSUQiLCB5ID0gIlRvdGFsIFNwZW5kaW5nIikgKyANCiAgdGhlbWVfY2xhc3NpYygpDQoNCiMgQ29udmVydCB0byBpbnRlcmFjdGl2ZSBwbG90IHVzaW5nIGdncGxvdGx5DQpnZ3Bsb3RseSh2aXNfdG9wX2N1c3RtZXJzKQ0KDQpgYGANCiMgT2JzZXJ2YXRpb246IFRoZSB0b3AgMTAgY3VzdG9tZXJzIGNvbnRyaWJ1dGUgYSBzaWduaWZpY2FudCBwb3J0aW9uIG9mIHRoZSB0b3RhbCByZXZlbnVlLg0KIyBJbnRlcnByZXRhdGlvbjogQSBzbWFsbCBudW1iZXIgb2YgaGlnaC12YWx1ZSBjdXN0b21lcnMgYXJlIHJlc3BvbnNpYmxlIGZvciBhIGxhcmdlIHNoYXJlIG9mIHNhbGVzLCB3aGljaCBjb3VsZCBpbmRpY2F0ZSB0aGUgaW1wb3J0YW5jZSBvZiBjdXN0b21lciByZXRlbnRpb24gYW5kIGxveWFsdHkgcHJvZ3JhbXMuDQoNCg0KIyMjIFBST0RVQ1QgQU5BTFlTSVMgIyMjDQpgYGB7cn0NCiMgVG9wIDEwIHByb2R1Y3RzIGJ5IHF1YW50aXR5IHNvbGQNCnRvcF9wcm9kdWN0c19xdWFudGl0eSA8LSB2aXN1YWxpemVfZGYgJT4lDQogIGdyb3VwX2J5KFN0b2NrQ29kZSwgRGVzY3JpcHRpb24pICU+JQ0KICBzdW1tYXJpc2UoVG90YWxRdWFudGl0eSA9IHN1bShRdWFudGl0eSksIC5ncm91cHMgPSAiZHJvcCIpICU+JQ0KICBhcnJhbmdlKGRlc2MoVG90YWxRdWFudGl0eSkpICU+JQ0KICBoZWFkKDEwKQ0KDQojIFBsb3QgVG9wIDEwIFByb2R1Y3RzIGJ5IFF1YW50aXR5IFNvbGQNCnZpc190b3BfcHJvZHVjdHNfcXVhbnRpdHkgPC0gZ2dwbG90KHRvcF9wcm9kdWN0c19xdWFudGl0eSwgYWVzKHggPSByZW9yZGVyKERlc2NyaXB0aW9uLCBUb3RhbFF1YW50aXR5KSwgeSA9IFRvdGFsUXVhbnRpdHkpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gInllbGxvdyIpICsNCiAgY29vcmRfZmxpcCgpICsNCiAgbGFicyh0aXRsZSA9ICJUb3AgMTAgUHJvZHVjdHMgYnkgUXVhbnRpdHkgU29sZCIsIHggPSAiUHJvZHVjdCBEZXNjcmlwdGlvbiIsIHkgPSAiVG90YWwgUXVhbnRpdHkgU29sZCIpICsNCiAgdGhlbWVfY2xhc3NpYygpDQoNCiMgQ29udmVydCB0byBpbnRlcmFjdGl2ZSBwbG90IHVzaW5nIGdncGxvdGx5DQpnZ3Bsb3RseSh2aXNfdG9wX3Byb2R1Y3RzX3F1YW50aXR5KQ0KYGBgDQojIE9ic2VydmF0aW9uOiBBIHNtYWxsIHNldCBvZiBwcm9kdWN0cyBtYWtlIHVwIGEgbGFyZ2UgcHJvcG9ydGlvbiBvZiB0aGUgdG90YWwgcXVhbnRpdHkgc29sZC4NCiMgSW50ZXJwcmV0YXRpb246IFRoZXNlIHByb2R1Y3RzIGFyZSBsaWtlbHkgcG9wdWxhciBvciBmcmVxdWVudGx5IHB1cmNoYXNlZCBpbiBidWxrLCBpbmRpY2F0aW5nIHRoZWlyIGltcG9ydGFuY2UgaW4gZHJpdmluZyBzYWxlcyB2b2x1bWUgZm9yIHRoZSBzdG9yZS4NCg0KYGBge3J9DQojIFRvcCAxMCBwcm9kdWN0cyBieSB0b3RhbCByZXZlbnVlIChUb3RhbFByaWNlKQ0KdG9wX3Byb2R1Y3RzX3JldmVudWUgPC0gdmlzdWFsaXplX2RmICU+JQ0KICBncm91cF9ieShTdG9ja0NvZGUsIERlc2NyaXB0aW9uKSAlPiUNCiAgc3VtbWFyaXNlKFRvdGFsUmV2ZW51ZSA9IHN1bShUb3RhbFByaWNlKSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lDQogIGFycmFuZ2UoZGVzYyhUb3RhbFJldmVudWUpKSAlPiUNCiAgaGVhZCgxMCkNCg0KIyBQbG90IFRvcCAxMCBQcm9kdWN0cyBieSBUb3RhbCBSZXZlbnVlDQp2aXNfdG9wX3Byb2R1Y3RfcmV2ZW51ZSA8LSBnZ3Bsb3QodG9wX3Byb2R1Y3RzX3JldmVudWUsIGFlcyh4ID0gcmVvcmRlcihEZXNjcmlwdGlvbiwgVG90YWxSZXZlbnVlKSwgeSA9IFRvdGFsUmV2ZW51ZSkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGwgPSAicHVycGxlIikgKw0KICBjb29yZF9mbGlwKCkgKw0KICBsYWJzKHRpdGxlID0gIlRvcCAxMCBQcm9kdWN0cyBieSBSZXZlbnVlIiwgeCA9ICJQcm9kdWN0IERlc2NyaXB0aW9uIiwgeSA9ICJUb3RhbCBSZXZlbnVlIikgKw0KICB0aGVtZV9jbGFzc2ljKCkNCg0KIyBDb252ZXJ0IHRvIGludGVyYWN0aXZlIHBsb3QgdXNpbmcgZ2dwbG90bHkNCmdncGxvdGx5KHZpc190b3BfcHJvZHVjdF9yZXZlbnVlKQ0KYGBgDQojIE9ic2VydmF0aW9uOiBBIGZldyBwcm9kdWN0cyBjb250cmlidXRlIGRpc3Byb3BvcnRpb25hdGVseSB0byB0aGUgdG90YWwgcmV2ZW51ZS4NCiMgSW50ZXJwcmV0YXRpb246IFRoZXNlIGhpZ2gtcmV2ZW51ZSBpdGVtcyBhcmUgdGhlIHN0b3JlJ3Mga2V5IGRyaXZlcnMgb2YgcHJvZml0YWJpbGl0eSBhbmQgY291bGQgYmUgYSBmb2N1cyBmb3IgbWFya2V0aW5nIG9yIHByb21vdGlvbnMgdG8gaW5jcmVhc2Ugc2FsZXMuDQoNCmBgYHtyfQ0KIyBUb3RhbCBzYWxlcyBieSBjb3VudHJ5DQpzYWxlc19ieV9jb3VudHJ5IDwtIHZpc3VhbGl6ZV9kZiAlPiUNCiAgZ3JvdXBfYnkoQ291bnRyeSkgJT4lDQogIHN1bW1hcmlzZShUb3RhbFNhbGVzID0gc3VtKFRvdGFsUHJpY2UpKQ0KDQojIFRvcCAxMCBjb3VudHJpZXMgYnkgdG90YWwgc2FsZXMNCnRvcF9jb3VudHJpZXMgPC0gc2FsZXNfYnlfY291bnRyeSAlPiUNCiAgYXJyYW5nZShkZXNjKFRvdGFsU2FsZXMpKSAlPiUNCiAgaGVhZCgxMCkNCg0KIyBQbG90IFRvcCAxMCBDb3VudHJpZXMgYnkgVG90YWwgU2FsZXMNCnRvcF9jb3VudHJpZXMgPC0gZ2dwbG90KHRvcF9jb3VudHJpZXMsIGFlcyh4ID0gcmVvcmRlcihDb3VudHJ5LCBUb3RhbFNhbGVzKSwgeSA9IFRvdGFsU2FsZXMpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gInJlZCIpICsNCiAgY29vcmRfZmxpcCgpICsNCiAgbGFicyh0aXRsZSA9ICJUb3AgMTAgQ291bnRyaWVzIGJ5IFRvdGFsIFNhbGVzIiwgeCA9ICJDb3VudHJ5IiwgeSA9ICJUb3RhbCBTYWxlcyIpICsNCiAgdGhlbWVfY2xhc3NpYygpDQoNCiMgQ29udmVydCB0byBpbnRlcmFjdGl2ZSBwbG90IHVzaW5nIGdncGxvdGx5DQpnZ3Bsb3RseSh0b3BfY291bnRyaWVzKQ0KDQpgYGANCiMgT2JzZXJ2YXRpb246IFRoZSBtYWpvcml0eSBvZiBzYWxlcyBhcmUgY29uY2VudHJhdGVkIGluIGEgc21hbGwgbnVtYmVyIG9mIGNvdW50cmllcy4NCiMgSW50ZXJwcmV0YXRpb246IFRoZSBzdG9yZSBsaWtlbHkgb3BlcmF0ZXMgaW4ga2V5IG1hcmtldHMgd2hlcmUgaXQgaGFzIGVzdGFibGlzaGVkIHN0cm9uZyBjdXN0b21lciBiYXNlcywgYnV0IG1heSBoYXZlIG9wcG9ydHVuaXRpZXMgdG8gZXhwYW5kIHNhbGVzIGluIHVuZGVycmVwcmVzZW50ZWQgcmVnaW9ucy4NCg0KDQoNCiMjIyBUSU1FIFNFUklFUyBBTkFMWVNJUyANCg0KYGBge3J9DQojIENvbnZlcnQgSW52b2ljZURhdGUgZnJvbSBjaGFyYWN0ZXIgdG8gUE9TSVhjdCBmb3JtYXQNCnZpc3VhbGl6ZV9kZiRJbnZvaWNlRGF0ZSA8LSBhcy5QT1NJWGN0KHZpc3VhbGl6ZV9kZiRJbnZvaWNlRGF0ZSwgZm9ybWF0ID0gIiVZLSVtLSVkICVIOiVNOiVTIikNCg0KIyBFeHRyYWN0IGp1c3QgdGhlIGRhdGUgKHdpdGhvdXQgdGltZSkgZm9yIGRhaWx5IGFnZ3JlZ2F0aW9uDQp2aXN1YWxpemVfZGYkRGF0ZU9ubHkgPC0gYXMuRGF0ZSh2aXN1YWxpemVfZGYkSW52b2ljZURhdGUpDQoNCiMgQWdncmVnYXRlIHRoZSBkYXRhIGJ5IGRheSB0byBjYWxjdWxhdGUgdG90YWwgc2FsZXMgcGVyIGRheQ0KZGFpbHlfc2FsZXMgPC0gdmlzdWFsaXplX2RmICU+JQ0KICBncm91cF9ieShEYXRlT25seSkgJT4lDQogIHN1bW1hcmlzZShUb3RhbFNhbGVzID0gc3VtKFRvdGFsUHJpY2UpKQ0KDQojIFRha2UgYSBsb29rIGF0IHRoZSBmaXJzdCBmZXcgcm93cw0KaGVhZChkYWlseV9zYWxlcykNCmBgYA0KYGBge3J9DQojIENyZWF0ZSBhIGxpbmUgcGxvdCBvZiB0b3RhbCBzYWxlcyBvdmVyIHRpbWUgKGRhaWx5KQ0KdmlzX2RhdGVfZGFpbHkgPC0gZ2dwbG90KGRhaWx5X3NhbGVzLCBhZXMoeCA9IERhdGVPbmx5LCB5ID0gVG90YWxTYWxlcykpICsNCiAgZ2VvbV9saW5lKGNvbG9yID0gImdyZWVuIiwgbGluZXdpZHRoID0gMC41KSArDQogIGxhYnModGl0bGUgPSAiVG90YWwgU2FsZXMgT3ZlciBUaW1lIChEYWlseSkiLCB4ID0gIkRhdGUiLCB5ID0gIlRvdGFsIFNhbGVzIChSZXZlbnVlKSIpICsNCiAgdGhlbWVfY2xhc3NpYygpDQoNCiMgQ29udmVydCB0byBhbiBpbnRlcmFjdGl2ZSBwbG90IHVzaW5nIGdncGxvdGx5DQpnZ3Bsb3RseSh2aXNfZGF0ZV9kYWlseSkNCg0KYGBgDQojIE9ic2VydmF0aW9uOiBUaGUgcGxvdCBzaG93cyBmbHVjdHVhdGlvbnMgaW4gZGFpbHkgc2FsZXMsIHdpdGggbm90aWNlYWJsZSBwZWFrcyBkdXJpbmcgY2VydGFpbiBwZXJpb2RzLg0KIyBJbnRlcnByZXRhdGlvbjogU2FsZXMgbWF5IGJlIGluZmx1ZW5jZWQgYnkgZmFjdG9ycyBzdWNoIGFzIHByb21vdGlvbnMsIGhvbGlkYXlzLCBvciBzZWFzb25hbCB0cmVuZHMsIHdpdGggcGVha3MgcG90ZW50aWFsbHkgY29ycmVzcG9uZGluZyB0byBoaWdoLWRlbWFuZCBwZXJpb2RzLg0KDQpgYGB7cn0NCiMgQWdncmVnYXRlIHRoZSBkYXRhIGJ5IG1vbnRoIHRvIGNhbGN1bGF0ZSB0b3RhbCBzYWxlcyBwZXIgbW9udGgNCm1vbnRobHlfc2FsZXMgPC0gdmlzdWFsaXplX2RmICU+JQ0KICBtdXRhdGUoTW9udGggPSBmb3JtYXQoSW52b2ljZURhdGUsICIlWS0lbSIpKSAlPiUNCiAgZ3JvdXBfYnkoTW9udGgpICU+JQ0KICBzdW1tYXJpc2UoVG90YWxTYWxlcyA9IHN1bShUb3RhbFByaWNlKSkNCg0KIyBDb252ZXJ0ICdNb250aCcgaW50byBhIGRhdGUgYnkgYXBwZW5kaW5nICctMDEnIHRvIGVhY2ggdmFsdWUNCm1vbnRobHlfc2FsZXMkTW9udGggPC0gYXMuRGF0ZShwYXN0ZTAobW9udGhseV9zYWxlcyRNb250aCwgIi0wMSIpKQ0KYGBgDQpgYGB7cn0NCiMgQ3JlYXRlIGEgbGluZSBwbG90IG9mIHRvdGFsIHNhbGVzIG92ZXIgdGltZSAobW9udGhseSkNCnZpc19kYXRlX21vbnRobHkgPC0gZ2dwbG90KG1vbnRobHlfc2FsZXMsIGFlcyh4ID0gTW9udGgsIHkgPSBUb3RhbFNhbGVzKSkgKw0KICBnZW9tX2xpbmUoY29sb3IgPSAiZ3JlZW4iLCBzaXplID0gMC41KSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpICsgICMgUm90YXRlIHgtYXhpcyBsYWJlbHMNCiAgbGFicyh0aXRsZSA9ICJUb3RhbCBTYWxlcyBPdmVyIFRpbWUgKE1vbnRobHkpIiwgeCA9ICJNb250aCIsIHkgPSAiVG90YWwgU2FsZXMgKFJldmVudWUpIikgKw0KICB0aGVtZV9saW5lZHJhdygpDQoNCiMgQ29udmVydCB0byBhbiBpbnRlcmFjdGl2ZSBwbG90IHVzaW5nIGdncGxvdGx5DQpnZ3Bsb3RseSh2aXNfZGF0ZV9tb250aGx5KQ0KDQpgYGANCiMgT2JzZXJ2YXRpb246IFRoZSBtb250aGx5IHNhbGVzIHBsb3Qgc21vb3RocyBvdXQgdGhlIGRhaWx5IGZsdWN0dWF0aW9ucywgc2hvd2luZyBicm9hZGVyIHRyZW5kcyBhbmQgcG90ZW50aWFsIHNlYXNvbmFsaXR5IGluIHRoZSBkYXRhLg0KIyBJbnRlcnByZXRhdGlvbjogVGhlIGRhdGEgaW5kaWNhdGVzIHBlcmlvZHMgb2Ygc3RlYWR5IGdyb3d0aCBhbmQgcG9zc2libGUgc2Vhc29uYWwgc3Bpa2VzLCB1c2VmdWwgZm9yIGZvcmVjYXN0aW5nIGZ1dHVyZSBzYWxlcyBhbmQgbWFuYWdpbmcgaW52ZW50b3J5Lg0KDQpgYGB7cn0NCiMgQ3JlYXRlIGEgbGluZSBwbG90IG9mIHRvdGFsIHNhbGVzIG92ZXIgdGltZSAoZGFpbHkpDQp2aXNfZGF0ZV9kYWlseV9zbSA8LSBnZ3Bsb3QoZGFpbHlfc2FsZXMsIGFlcyh4ID0gRGF0ZU9ubHksIHkgPSBUb3RhbFNhbGVzKSkgKw0KICBnZW9tX2xpbmUoY29sb3IgPSAiZ3JlZW4iLCBzaXplID0gMC41KSArDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsb2VzcyIsIGNvbG9yID0gImRhcmtncmVlbiIsIHNpemUgPSAwLjMpICsNCiAgbGFicyh0aXRsZSA9ICJUb3RhbCBTYWxlcyBPdmVyIFRpbWUgKERhaWx5KSIsIHggPSAiRGF0ZSIsIHkgPSAiVG90YWwgU2FsZXMgKFJldmVudWUpIikgKw0KICB0aGVtZV9jbGFzc2ljKCkNCg0KIyBDb252ZXJ0IHRvIGFuIGludGVyYWN0aXZlIHBsb3QgdXNpbmcgZ2dwbG90bHkNCmdncGxvdGx5KHZpc19kYXRlX2RhaWx5X3NtKQ0KYGBgDQojIE9ic2VydmF0aW9uOiBUaGUgbGluZSBwbG90IHdpdGggdGhlIHNtb290aGluZyBsaW5lIHNob3dzIGZsdWN0dWF0aW9ucyBpbiBkYWlseSBzYWxlcywgd2l0aCBzb21lIGRpc3RpbmN0IHBlYWtzLg0KIyBJbnRlcnByZXRhdGlvbjogVGhlIHBlYWtzIGxpa2VseSBjb3JyZXNwb25kIHRvIGhpZ2ggc2FsZXMgZGF5cywgcG9zc2libHkgZHJpdmVuIGJ5IHByb21vdGlvbmFsIGV2ZW50cyBvciBob2xpZGF5cy4gVGhlIHNtb290aCBsaW5lIHByb3ZpZGVzIGFuIG92ZXJhbGwgdHJlbmQsIHNob3dpbmcgZ3Jvd3RoIG9yIGRlY2xpbmUgcGF0dGVybnMgaW4gc2FsZXMgb3ZlciB0aW1lLg0KDQpgYGB7cn0NCiMgRXh0cmFjdCB0aGUgZGF5IG9mIHRoZSB3ZWVrIChlLmcuLCBTdW5kYXksIE1vbmRheSkNCnZpc3VhbGl6ZV9kZiREYXlPZldlZWsgPC0gd2Vla2RheXModmlzdWFsaXplX2RmJERhdGVPbmx5KQ0KDQojIEFnZ3JlZ2F0ZSBkYXRhIGJ5IGRheSBvZiB0aGUgd2VlayB0byBjYWxjdWxhdGUgdG90YWwgdHJhZmZpYyAocXVhbnRpdHkgc29sZCkNCndlZWtseV90cmFmZmljIDwtIHZpc3VhbGl6ZV9kZiAlPiUNCiAgZ3JvdXBfYnkoRGF5T2ZXZWVrKSAlPiUNCiAgc3VtbWFyaXNlKFRvdGFsVHJhZmZpYyA9IHN1bShRdWFudGl0eSkpDQoNCiMgRW5zdXJlIHRoZSBkYXlzIG9mIHRoZSB3ZWVrIGFyZSBpbiB0aGUgY29ycmVjdCBvcmRlcg0KZGF5c19vcmRlciA8LSBjKCJTdW5kYXkiLCAiTW9uZGF5IiwgIlR1ZXNkYXkiLCAiV2VkbmVzZGF5IiwgIlRodXJzZGF5IiwgIkZyaWRheSIsICJTYXR1cmRheSIpDQp3ZWVrbHlfdHJhZmZpYyREYXlPZldlZWsgPC0gZmFjdG9yKHdlZWtseV90cmFmZmljJERheU9mV2VlaywgbGV2ZWxzID0gZGF5c19vcmRlcikNCg0KIyBQcmludCB0aGUgYWdncmVnYXRlZCB3ZWVrbHkgdHJhZmZpYyBkYXRhDQpwcmludCh3ZWVrbHlfdHJhZmZpYykNCmBgYA0KDQpgYGB7cn0NCiMgQ3JlYXRlIGEgYmFyIHBsb3QgdG8gdmlzdWFsaXplIHRyYWZmaWMgYnkgZGF5IG9mIHRoZSB3ZWVrDQp0cmFmZmljX2J5X2RheV9wbG90IDwtIGdncGxvdCh3ZWVrbHlfdHJhZmZpYywgYWVzKHggPSBEYXlPZldlZWssIHkgPSBUb3RhbFRyYWZmaWMsIGZpbGwgPSBEYXlPZldlZWspKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArICAjIEJhciBwbG90DQogIGxhYnModGl0bGUgPSAiVG90YWwgVHJhZmZpYyAoUXVhbnRpdHkgU29sZCkgYnkgRGF5IG9mIHRoZSBXZWVrIiwgeCA9ICJEYXkgb2YgdGhlIFdlZWsiLCB5ID0gIlRvdGFsIFRyYWZmaWMgKFF1YW50aXR5IFNvbGQpIikgKw0KICB0aGVtZV9jbGFzc2ljKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCiMgQ29udmVydCB0byBhbiBpbnRlcmFjdGl2ZSBwbG90IHVzaW5nIGdncGxvdGx5DQpnZ3Bsb3RseSh0cmFmZmljX2J5X2RheV9wbG90KQ0KDQpgYGANCiMgT2JzZXJ2YXRpb246IFNhbGVzIHRyYWZmaWMgcGVha3Mgb24gY2VydGFpbiBkYXlzIG9mIHRoZSB3ZWVrLCB3aXRoIG5vdGljZWFibHkgaGlnaGVyIGFjdGl2aXR5Lg0KIyBJbnRlcnByZXRhdGlvbjogQ3VzdG9tZXIgYmVoYXZpb3Igc2hvd3MgdGhhdCBzcGVjaWZpYyBkYXlzIGRyaXZlIG1vcmUgc2FsZXMsIHdoaWNoIGNhbiBoZWxwIGluIG9wdGltaXppbmcgc3RhZmZpbmcsIHByb21vdGlvbnMsIGFuZCBvcGVyYXRpb25zIGZvciB0aG9zZSBoaWdoLXRyYWZmaWMgZGF5cy4NCg0KYGBge3J9DQojIEV4dHJhY3QgdGhlIGhvdXIgb2YgdGhlIGRheSBmcm9tIHRoZSBJbnZvaWNlRGF0ZQ0KdmlzdWFsaXplX2RmJEhvdXIgPC0gZm9ybWF0KHZpc3VhbGl6ZV9kZiRJbnZvaWNlRGF0ZSwgIiVIIikNCg0KIyBBZ2dyZWdhdGUgZGF0YSBieSBob3VyIG9mIHRoZSBkYXkgdG8gY2FsY3VsYXRlIHRvdGFsIHNhbGVzDQpob3VybHlfc2FsZXMgPC0gdmlzdWFsaXplX2RmICU+JQ0KICBncm91cF9ieShIb3VyKSAlPiUNCiAgc3VtbWFyaXNlKFRvdGFsU2FsZXMgPSBzdW0oVG90YWxQcmljZSkpDQoNCiMgUGxvdCB0b3RhbCBzYWxlcyBieSBob3VyDQp2aXNfc2FsZXNfYnlfaG91ciA8LSBnZ3Bsb3QoaG91cmx5X3NhbGVzLCBhZXMoeCA9IEhvdXIsIHkgPSBUb3RhbFNhbGVzKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgZmlsbCA9ICJibHVlIikgKw0KICBsYWJzKHRpdGxlID0gIlRvdGFsIFNhbGVzIGJ5IEhvdXIgb2YgRGF5IiwgeCA9ICJIb3VyIiwgeSA9ICJUb3RhbCBTYWxlcyAoUmV2ZW51ZSkiKSArDQogIHRoZW1lX2NsYXNzaWMoKQ0KDQojIENvbnZlcnQgdG8gYW4gaW50ZXJhY3RpdmUgcGxvdCB1c2luZyBnZ3Bsb3RseQ0KZ2dwbG90bHkodmlzX3NhbGVzX2J5X2hvdXIpDQoNCmBgYA0KIyBPYnNlcnZhdGlvbjogU2FsZXMgYWN0aXZpdHkgdmFyaWVzIHRocm91Z2hvdXQgdGhlIGRheSwgd2l0aCBwZWFrcyBhdCBjZXJ0YWluIGhvdXJzLg0KIyBJbnRlcnByZXRhdGlvbjogVGhpcyBhbmFseXNpcyBpcyBjcnVjaWFsIGZvciB1bmRlcnN0YW5kaW5nIHBlYWsgc2hvcHBpbmcgdGltZXMsIGFsbG93aW5nIHRoZSBzdG9yZSB0byBvcHRpbWl6ZSByZXNvdXJjZXMsIGFkdmVydGlzaW5nLCBhbmQgY3VzdG9tZXIgc2VydmljZSBkdXJpbmcgdGhlIGJ1c2llc3QgcGVyaW9kcy4NCmBgYHtyfQ0KIyBBZ2dyZWdhdGUgc2FsZXMgYnkgZGF0ZSBhbmQgY291bnRyeQ0Kc2FsZXNfYnlfY291bnRyeV9vdmVyX3RpbWUgPC0gdmlzdWFsaXplX2RmICU+JQ0KICBncm91cF9ieShEYXRlT25seSwgQ291bnRyeSkgJT4lDQogIHN1bW1hcmlzZShUb3RhbFNhbGVzID0gc3VtKFRvdGFsUHJpY2UpLCAuZ3JvdXBzID0gImRyb3AiKQ0KDQojIEZpbHRlciB0byB0b3AgNSBjb3VudHJpZXMgZm9yIGJldHRlciBjb21wYXJpc29uDQp0b3BfNV9jb3VudHJpZXMgPC0gdmlzdWFsaXplX2RmICU+JQ0KICBncm91cF9ieShDb3VudHJ5KSAlPiUNCiAgc3VtbWFyaXNlKFRvdGFsU2FsZXMgPSBzdW0oVG90YWxQcmljZSkpICU+JQ0KICBhcnJhbmdlKGRlc2MoVG90YWxTYWxlcykpICU+JQ0KICBoZWFkKDUpDQoNCiMgRmlsdGVyIGRhdGEgZm9yIHRoZSB0b3AgNSBjb3VudHJpZXMNCnNhbGVzX3RvcF81X2NvdW50cmllcyA8LSBzYWxlc19ieV9jb3VudHJ5X292ZXJfdGltZSAlPiUNCiAgZmlsdGVyKENvdW50cnkgJWluJSB0b3BfNV9jb3VudHJpZXMkQ291bnRyeSkNCg0KIyBQbG90IHNhbGVzIHRyZW5kcyBieSBjb3VudHJ5IG92ZXIgdGltZQ0KdmlzX3NhbGVzX2J5X2NvdW50cnlfdGltZSA8LSBnZ3Bsb3Qoc2FsZXNfdG9wXzVfY291bnRyaWVzLCBhZXMoeCA9IERhdGVPbmx5LCB5ID0gVG90YWxTYWxlcywgY29sb3IgPSBDb3VudHJ5KSkgKw0KICBnZW9tX2xpbmUoKSArDQogIGxhYnModGl0bGUgPSAiU2FsZXMgVHJlbmRzIE92ZXIgVGltZSBieSBDb3VudHJ5IiwgeCA9ICJEYXRlIiwgeSA9ICJUb3RhbCBTYWxlcyAoUmV2ZW51ZSkiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQojIENvbnZlcnQgdG8gaW50ZXJhY3RpdmUgcGxvdCB1c2luZyBnZ3Bsb3RseQ0KZ2dwbG90bHkodmlzX3NhbGVzX2J5X2NvdW50cnlfdGltZSkNCg0KYGBgDQojIE9ic2VydmF0aW9uOiBTYWxlcyB0cmVuZHMgdmFyeSBhY3Jvc3MgdGhlIHRvcCA1IGNvdW50cmllcywgd2l0aCBjZXJ0YWluIHBlcmlvZHMgc2hvd2luZyBzeW5jaHJvbml6ZWQgcGVha3MgYWNyb3NzIG11bHRpcGxlIGNvdW50cmllcy4NCiMgSW50ZXJwcmV0YXRpb246IFRoaXMgYW5hbHlzaXMgcmV2ZWFscyBnbG9iYWwgc2FsZXMgdHJlbmRzIGFuZCBoaWdobGlnaHRzIHRoZSBtb3N0IGFjdGl2ZSBtYXJrZXRzIGR1cmluZyBzcGVjaWZpYyBwZXJpb2RzLiBJbnNpZ2h0cyBjYW4gYmUgdXNlZCB0byBpZGVudGlmeSBjb3VudHJ5LXNwZWNpZmljIGRlbWFuZCBjeWNsZXMgYW5kIGFkanVzdCBpbnZlbnRvcnkgb3IgbWFya2V0aW5nIGVmZm9ydHMgYWNjb3JkaW5nbHkuDQoNCmBgYHtyfQ0KIyBDYXRlZ29yaXplIGN1c3RvbWVycyBiYXNlZCBvbiB0b3RhbCBzcGVuZGluZw0KY3VzdG9tZXJfc2VnbWVudHMgPC0gdmlzdWFsaXplX2RmICU+JQ0KICBncm91cF9ieShDdXN0b21lci5JRCkgJT4lDQogIHN1bW1hcmlzZShUb3RhbFNwZW50ID0gc3VtKFRvdGFsUHJpY2UpKSAlPiUNCiAgbXV0YXRlKFNlZ21lbnQgPSBjYXNlX3doZW4oDQogICAgVG90YWxTcGVudCA8IDEwMCB+ICJMb3cgU3BlbmRlcnMiLA0KICAgIFRvdGFsU3BlbnQgPj0gMTAwICYgVG90YWxTcGVudCA8IDUwMCB+ICJNZWRpdW0gU3BlbmRlcnMiLA0KICAgIFRvdGFsU3BlbnQgPj0gNTAwIH4gIkhpZ2ggU3BlbmRlcnMiDQogICkpDQoNCiMgVmlzdWFsaXplIHRoZSBudW1iZXIgb2YgY3VzdG9tZXJzIGluIGVhY2ggc2VnbWVudA0KdmlzX2N1c3RvbWVyX3NlZ21lbnRzIDwtIGdncGxvdChjdXN0b21lcl9zZWdtZW50cywgYWVzKHggPSBTZWdtZW50LCBmaWxsID0gU2VnbWVudCkpICsNCiAgZ2VvbV9iYXIoKSArDQogIGxhYnModGl0bGUgPSAiQ3VzdG9tZXIgU2VnbWVudHMgQmFzZWQgb24gVG90YWwgU3BlbmRpbmciLCB4ID0gIlNlZ21lbnQiLCB5ID0gIk51bWJlciBvZiBDdXN0b21lcnMiKSArDQogIHRoZW1lX2NsYXNzaWMoKQ0KDQojIENvbnZlcnQgdG8gYW4gaW50ZXJhY3RpdmUgcGxvdCB1c2luZyBnZ3Bsb3RseQ0KZ2dwbG90bHkodmlzX2N1c3RvbWVyX3NlZ21lbnRzKQ0KDQpgYGANCiMgT2JzZXJ2YXRpb246IFRoZSBtYWpvcml0eSBvZiBjdXN0b21lcnMgYXJlIGxvdyBzcGVuZGVycywgd2l0aCBmZXdlciBtZWRpdW0gYW5kIGhpZ2ggc3BlbmRlcnMuDQojIEludGVycHJldGF0aW9uOiBUaGUgc3RvcmUgaGFzIGEgYnJvYWQgY3VzdG9tZXIgYmFzZSwgYnV0IG1vc3Qgb2YgdGhlIHJldmVudWUgaXMgbGlrZWx5IGNvbWluZyBmcm9tIGEgc21hbGwgbnVtYmVyIG9mIGhpZ2gtc3BlbmRpbmcgY3VzdG9tZXJzLiBSZXRhaW5pbmcgaGlnaCBzcGVuZGVycyBhbmQgY29udmVydGluZyBtb3JlIGxvdyBzcGVuZGVycyB0byBtZWRpdW0gb3IgaGlnaCBzcGVuZGVycyBzaG91bGQgYmUgYSBwcmlvcml0eS4NCg0KYGBge3J9DQojIENvdW50IHRoZSBudW1iZXIgb2Ygb3JkZXJzIHBlciBwcm9kdWN0IChpbnN0ZWFkIG9mIHRvdGFsIHF1YW50aXR5IHNvbGQpDQp0b3BfcHJvZHVjdHNfb3JkZXJzIDwtIHZpc3VhbGl6ZV9kZiAlPiUNCiAgZ3JvdXBfYnkoU3RvY2tDb2RlLCBEZXNjcmlwdGlvbikgJT4lDQogIHN1bW1hcmlzZShPcmRlckNvdW50ID0gbigpLCAuZ3JvdXBzID0gImRyb3AiKSAlPiUNCiAgYXJyYW5nZShkZXNjKE9yZGVyQ291bnQpKSAlPiUNCiAgaGVhZCgxMCkNCg0KIyBQbG90IHRvcCBwcm9kdWN0cyBiYXNlZCBvbiBudW1iZXIgb2Ygb3JkZXJzDQp2aXNfdG9wX3Byb2R1Y3RzX29yZGVycyA8LSBnZ3Bsb3QodG9wX3Byb2R1Y3RzX29yZGVycywgYWVzKHggPSByZW9yZGVyKERlc2NyaXB0aW9uLCBPcmRlckNvdW50KSwgeSA9IE9yZGVyQ291bnQpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gIm9yYW5nZSIpICsNCiAgY29vcmRfZmxpcCgpICsNCiAgbGFicyh0aXRsZSA9ICJUb3AgUHJvZHVjdHMgQmFzZWQgb24gTnVtYmVyIG9mIE9yZGVycyIsIHggPSAiUHJvZHVjdCBEZXNjcmlwdGlvbiIsIHkgPSAiTnVtYmVyIG9mIE9yZGVycyIpICsNCiAgdGhlbWVfY2xhc3NpYygpDQoNCiMgQ29udmVydCB0byBhbiBpbnRlcmFjdGl2ZSBwbG90IHVzaW5nIGdncGxvdGx5DQpnZ3Bsb3RseSh2aXNfdG9wX3Byb2R1Y3RzX29yZGVycykNCg0KYGBgDQojIE9ic2VydmF0aW9uOiBBIGZldyBwcm9kdWN0cyBhcmUgb3JkZXJlZCBtdWNoIG1vcmUgZnJlcXVlbnRseSB0aGFuIG90aGVycy4NCiMgSW50ZXJwcmV0YXRpb246IFRoZXNlIHByb2R1Y3RzIGFyZSBwb3B1bGFyIHdpdGggY3VzdG9tZXJzIGFuZCBsaWtlbHkgcmVwcmVzZW50IGVzc2VudGlhbCBvciBmYXN0LXNlbGxpbmcgaXRlbXMuIFRoZSBzdG9yZSBtYXkgYmVuZWZpdCBmcm9tIHN0b2NraW5nIHRoZXNlIGl0ZW1zIGluIGxhcmdlciBxdWFudGl0aWVzIGFuZCBwcm9tb3RpbmcgdGhlbSBtb3JlIHRvIG1haW50YWluIGFuZCBncm93IHNhbGVzLg0K